import { Color, Component, Label, Material, Node, Sprite, UITransform, v2, v3, Vec2, Vec3 } from 'cc';
import { GameManager } from '../Basic/Manager/GameManager';
/* eslint-disable */

export namespace Tool {
  // export async function getSpriteByKey(altasPath: string, key: string) {
  //   if (altasPath === '' && key.indexOf('/') === -1) return null;
  //   if (altasPath != '') {
  //     const atlas = await LoadingManager.Ins().getLoadRes<SpriteAtlas>(altasPath);
  //     return atlas.getSpriteFrame(key);
  //   } else {
  //     return await LoadingManager.Ins().getLoadRes<SpriteFrame>(key + '/spriteFrame');
  //   }
  // }

  export function setGrayNode(node: Node, isGray: boolean = true) {
    const labCom = node.getComponent(Label);
    const sprCom = node.getComponent(Sprite);
    if (labCom) labCom.customMaterial = isGray ? GameManager.Singleton.grayMaterial : null;
    if (sprCom) sprCom.customMaterial = isGray ? GameManager.Singleton.grayMaterial : null;

    node.children.forEach((child) => {
      Tool.setGrayNode(child, isGray);
    });
  }

  /** 程序睡眠函数 */
  export function Sleep(ms: number) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  /**
   * 获取bbcode中
   * @param bbcode bbcode文本
   * @param tag 标签
   * @param attr
   * @returns 匹配的文本字段
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  export function getBBCodeImg(bbcode: string, tag: string, attr: string) {
    const imgReg = /<img.*?(?:>|\/>)/gi;
    // 提取src正则
    // eslint-disable-next-line no-useless-escape
    const srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i;
    const newStr = bbcode.slice();
    // 获取img标签数组
    const imgArr = newStr.match(imgReg);
    const srcArr = [];
    if (imgArr)
      for (let i = 0; i < imgArr.length; i++) {
        srcArr.push(imgArr[i].match(srcReg)[1]);
      }
    // imgArr是img标签集合，srcArr是图片src集合，newStr是替换后的新字符串
    return srcArr;
  }

  export function getCommonFactor(a: number, b: number) {
    for (let i = 2; i < 100; i++) {
      if (a / i > Math.floor(a / i) || b / i > Math.floor(b / i)) {
        continue;
      } else {
        // console.log(i);
        return Tool.getCommonFactor(a / i, b / i);
      }
    }
    return [a, b];
  }

  export function getNumCount(num: number, index = 1) {
    if (num / Math.pow(10, index) >= 1) {
      return Tool.getNumCount(num, ++index) + 1;
    } else {
      return 1;
    }
  }

  export function getDecimals(num: number) {
    return num - Math.floor(num);
  }

  export function getValueByRange(a: number, b: number, v: number) {
    if ((a > b && v >= a) || (a <= b && v <= a)) {
      return a;
    } else if ((a > b && v <= b) || (a <= b && v >= b)) {
      return b;
    } else {
      return v;
    }
  }

  export function Lerp(a: number, b: number, t: number) {
    return a + (b - a) * t;
  }

  export function LerpVec(v1: Vec2, v2: Vec2, t: number) {
    const v = new Vec2();
    v.x = Tool.Lerp(v1.x, v2.x, t);
    v.y = Tool.Lerp(v1.y, v2.y, t);
    return v;
  }

  export function buttonBindClickHandler(n: Node, c: string, h: string, ...args) {
    const eHandler = new Component.EventHandler();
    eHandler.target = n;
    eHandler.component = c;
    eHandler.handler = h;
    eHandler.customEventData = args.toString();
    return [eHandler];
  }

  export function Vec2Add(v1: Vec2, v2: Vec2) {
    return new Vec2(v1.x + v2.x, v1.y + v2.y);
  }

  export function Vec2Sub(v1: Vec2, v2: Vec2) {
    return new Vec2(v1.x - v2.x, v1.y - v2.y);
  }

  export function Vec2Mut(v: Vec2, s: number) {
    return new Vec2(v.x * s, v.y * s);
  }

  export function Vec2Nor(v: Vec2) {
    if (v.x > v.y) {
      return new Vec2(v.x / v.x, v.y / v.x);
    } else {
      return new Vec2(v.x / v.y, v.y / v.y);
    }
  }

  /**
   * 生成贝塞尔曲线的点
   * @param point1
   * @param point2
   * @param controlPoint1 point1的控制点
   * @param controlPoint2 point2的控制点
   * @param t 取值[0, 1] 设线段point1point2上的某动点p0，t = 线段point1p0长度 / 线段point1point2长度
   * @returns 返回曲线上的某个点
   */
  export function createBezierCurve(startPoint: Vec2, endPoint: Vec2, controlPoint1: Vec2, controlPoint2: Vec2, t: number) {
    const x = Math.pow(1 - t, 3) * startPoint.x + 3 * controlPoint1.x * t * Math.pow(1 - t, 2) + 3 * controlPoint2.x * t * t * (1 - t) + endPoint.x * t * t * t;
    const y = Math.pow(1 - t, 3) * startPoint.y + 3 * controlPoint1.y * t * Math.pow(1 - t, 2) + 3 * controlPoint2.y * t * t * (1 - t) + endPoint.y * t * t * t;
    return new Vec2(x, y);
  }

  /**
   * 贝塞尔曲线控制点计算
   * @param pointsArr 贝塞尔曲线所经过的点
   * @param smooth_value 控制曲线丝滑程度 取值[0, 1]
   * @returns
   */
  export function createBezierCurveControl(pointsArr: Vec2[], smooth_value: number) {
    const points = [...pointsArr];
    points.unshift(new Vec2(points[0].x - 50 * smooth_value, points[0].y + 50 * smooth_value));
    points.push(new Vec2(points[points.length - 1].x + 50 * smooth_value, points[points.length - 1].y + 50 * smooth_value));
    const controlPoints = [];
    const len = points.length - 3;
    for (let i = 0; i < len; i++) {
      const x0 = points[i].x;
      const y0 = points[i].y;
      const x1 = points[i + 1].x;
      const y1 = points[i + 1].y;
      const x2 = points[i + 2].x;
      const y2 = points[i + 2].y;
      const x3 = points[i + 3].x;
      const y3 = points[i + 3].y;
      // 2.求中点
      const xc1 = (x0 + x1) / 2.0;
      const yc1 = (y0 + y1) / 2.0;
      const xc2 = (x1 + x2) / 2.0;
      const yc2 = (y1 + y2) / 2.0;
      const xc3 = (x2 + x3) / 2.0;
      const yc3 = (y2 + y3) / 2.0;

      // 3.求各点连线长度
      const len1 = Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
      const len2 = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
      const len3 = Math.sqrt((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2));

      // 4.求连线长度比例（用来确定平移前p2, p3的位置）
      const k1 = len1 / (len1 + len2);
      const k2 = len2 / (len2 + len3);

      // 5.平移点(x1, y1)
      const xm1 = xc1 + (xc2 - xc1) * k1;
      const ym1 = yc1 + (yc2 - yc1) * k1;

      // 6.平移点(x2, y2)
      const xm2 = xc2 + (xc3 - xc2) * k2;
      const ym2 = yc2 + (yc3 - yc2) * k2;

      // 7.微调控制点与顶点之间的距离，越大曲线越平直
      const ctrl1_x = Math.round(xm1 + (xc2 - xm1) * smooth_value + x1 - xm1);
      const ctrl1_y = Math.round(ym1 + (yc2 - ym1) * smooth_value + y1 - ym1);

      const ctrl2_x = Math.round(xm2 + (xc2 - xm2) * smooth_value + x2 - xm2);
      const ctrl2_y = Math.round(ym2 + (yc2 - ym2) * smooth_value + y2 - ym2);

      controlPoints.push(new Vec2(ctrl1_x, ctrl1_y), new Vec2(ctrl2_x, ctrl2_y));
    }
    return controlPoints;
  }

  /**
   * 生成曲线的所有点
   * @param pointsArray
   * @param smoothValue 控制曲线的丝滑程度, 取值[0, 1]
   * @returns 返回曲线的所有点，使用画布绘制即可
   */
  export function createBezierCurvePoints(pointsArray: Vec2[], smoothValue: number) {
    const controlPoints = Tool.createBezierCurveControl(pointsArray, smoothValue);
    const bezierCurvePoints = [];
    for (let i = 0; i < pointsArray.length - 1; i++) {
      for (let j = 0; j <= 100; j++) {
        bezierCurvePoints.push(Tool.createBezierCurve(pointsArray[i], pointsArray[i + 1], controlPoints[2 * i], controlPoints[2 * i + 1], j / 100));
      }
    }
    return bezierCurvePoints;
  }

  /** 旋转平面向量 */
  export function rotateVec2(radius: number, dir: Vec2) {
    const v = new Vec2();
    v.x = dir.x * Math.cos(radius) - dir.y * Math.sin(radius);
    v.y = dir.y * Math.cos(radius) + dir.x * Math.sin(radius);
    return v;
  }

  /** 获取切线高度位置 */
  export function getTangentLinePos(p0: Vec2, p1: Vec2, radius: number, len: number) {
    const centenPos = Tool.Vec2Sub(p1, p0);
    const angle = (Math.atan2(centenPos.y, centenPos.x) * 180) / Math.PI;
    const ret = angle >= 90 || angle <= -90;
    const rot = Tool.Vec2Mut(Tool.rotateVec2(ret ? -radius : radius, centenPos).normalize(), len);
    return Tool.Vec2Add(p0, Tool.Vec2Add(centenPos, rot));
  }

  /** 获取切线高度位置 */
  export function getRandomBezierPos(p0: Vec2, p1: Vec2, radius: number, len: number) {
    const centenPos = Tool.Vec2Sub(p1, p0);
    const tempPos = Tool.Vec2Mut(centenPos.normalize(), Math.random() * centenPos.length());
    const ret = Math.random() > 0.5;
    const rot = Tool.Vec2Mut(Tool.rotateVec2(ret ? -radius : radius, centenPos).normalize(), len);
    return Tool.Vec2Add(p0, Tool.Vec2Add(tempPos, rot));
  }

  /**
   * @desc 获取点，这里可以设置点的个数
   * @param {number} num 点个数
   * @param {Array} p1 点坐标
   * @param {Array} p2 点坐标
   * @param {Array} p3 点坐标
   * @param {Array} p4 点坐标
   * 如果参数是 num, p1, p2 为一阶贝塞尔
   * 如果参数是 num, p1, c1, p2 为二阶贝塞尔
   * 如果参数是 num, p1, c1, c2, p2 为三阶贝塞尔
   */
  export function getBezierPoints(num = 100, p1: Vec2, p2: Vec2, p3: Vec2[], p4: Vec2[]) {
    let func = null;
    const points = [];
    if (!p3 && !p4) {
      func = Tool.oneBezier;
    } else if (p3 && !p4) {
      func = Tool.twoBezier;
    } else if (p3 && p4) {
      func = Tool.threeBezier;
    }
    for (let i = 0; i < num; i++) {
      points.push(func(i / num, p1, p2, p3, p4));
    }
    if (p4) {
      points.push(...p4);
    } else if (p3) {
      points.push(...p3);
    }
    return points;
  }

  /**
   * @desc 一阶贝塞尔
   * @param {number} t 当前百分比
   * @param {Array} p1 起点坐标
   * @param {Array} p2 终点坐标
   */
  export function oneBezier(t: number, p1: Vec2, p2: Vec2, out?: Vec2): Vec2;
  export function oneBezier(t: number, p1: Vec3, p2: Vec3, out?: Vec3): Vec3;
  export function oneBezier(t: number, p1: Vec2 | Vec3, p2: Vec2 | Vec3, out?: Vec2 | Vec3): Vec2 | Vec3 {
    if (!out) out = p1 instanceof Vec2 ? v2() : v3();
    out.x = p1.x + (p2.x - p1.x) * t;
    out.y = p1.y + (p2.y - p1.y) * t;
    return out;
  }

  /**
   * @desc 二阶贝塞尔
   * @param {number} t 当前百分比
   * @param {T} p1 起点坐标
   * @param {T} p2 终点坐标
   * @param {T} cp 控制点
   * @param {T} out 输出点
   */
  export function twoBezier(t: number, p1: Vec2, cp: Vec2, p2: Vec2, out?: Vec2): Vec2;
  export function twoBezier(t: number, p1: Vec3, cp: Vec3, p2: Vec3, out?: Vec3): Vec3;
  export function twoBezier(t: number, p1: Vec2 | Vec3, cp: Vec2 | Vec3, p2: Vec2 | Vec3, out?: Vec2 | Vec3): Vec2 | Vec3 {
    if (!out) out = p1 instanceof Vec2 ? v2() : v3();
    out.x = (1 - t) * (1 - t) * p1.x + 2 * t * (1 - t) * cp.x + t * t * p2.x;
    out.y = (1 - t) * (1 - t) * p1.y + 2 * t * (1 - t) * cp.y + t * t * p2.y;
    return out;
  }

  /**
   * @desc 三阶贝塞尔
   * @param {number} t 当前百分比
   * @param {T} p1 起点坐标
   * @param {T} p2 终点坐标
   * @param {T} cp1 控制点1
   * @param {T} cp2 控制点2
   * @param {T} out 输出点
   */
  export function threeBezier<T extends Vec2 | Vec3>(t: number, p1: T, cp1: T, cp2: T, p2: T, out: T) {
    out.x = p1.x * (1 - t) * (1 - t) * (1 - t) + 3 * cp1.x * t * (1 - t) * (1 - t) + 3 * cp2.x * t * t * (1 - t) + p2.x * t * t * t;
    out.y = p1.y * (1 - t) * (1 - t) * (1 - t) + 3 * cp1.y * t * (1 - t) * (1 - t) + 3 * cp2.y * t * t * (1 - t) + p2.y * t * t * t;
    return out;
  }

  // anchorpoints：贝塞尔基点
  // pointsAmount：生成的点数
  // return 路径点的Array
  export function CreateBezierPoints(anchorpoints: Vec2[], pointsAmount: number) {
    const points = [];
    for (let i = 0; i < pointsAmount; i++) {
      const point = Tool.MultiPointBezier(anchorpoints, i / pointsAmount);
      points.push(point);
    }
    return points;
  }

  export function MultiPointBezier(points: Vec2[], t: number) {
    const len = points.length;
    let x = 0;
    let y = 0;
    const erxiangshi = function (start, end) {
      let cs = 1;
      let bcs = 1;
      while (end > 0) {
        cs *= start;
        bcs *= end;
        start--;
        end--;
      }
      return cs / bcs;
    };
    for (let i = 0; i < len; i++) {
      const point = points[i];
      x += point.x * Math.pow(1 - t, len - 1 - i) * Math.pow(t, i) * erxiangshi(len - 1, i);
      y += point.y * Math.pow(1 - t, len - 1 - i) * Math.pow(t, i) * erxiangshi(len - 1, i);
    }
    return new Vec2(x, y);
  }

  export function MatchCode(str: string, num: number) {
    return String.fromCharCode(str.charCodeAt(0) + num);
  }

  export function CryptographicString(str: string, value = 0) {
    let tempStr = '';
    for (let i = 0; i < str.length; i++) {
      tempStr += Tool.MatchCode(str[i], value);
    }
    return tempStr;
  }

  export function DecryptionString(str: string, value = -0) {
    let tempStr = '';
    for (let i = 0; i < str.length; i++) {
      tempStr += Tool.MatchCode(str[i], value);
    }
    return tempStr;
  }

  export function colorToGrayScale(color: Color) {
    if (!color) return;
    const gray = color.r * 0.3 + color.g * 0.59 + color.b * 0.11;
    return new Color(gray, gray, gray);
  }

  /** 跟据数据的属性，对数据进行排序 */
  export function sortArray(arr: any[], key: any[], sort?: number[]): any[] {
    if (!sort) {
      // 默认从小到大排序
      sort = new Array(key.length).fill(1);
    }

    // 一次性完成所有键的排序
    arr.sort((a, b) => {
      for (let i = 0; i < key.length; i++) {
        const comparison = a[key[i]] - b[key[i]];
        if (comparison !== 0) {
          return comparison * sort[i];
        }
      }
      return 0;
    });

    return arr;
  }

  /**
   * 递归获取嵌套属性的值
   * @param obj       目标对象
   * @param key       属性路径，支持多层级属性，使用'.'分隔
   * @param separator 属性层级分隔符，默认为'.'
   * @returns
   */
  export function getNestedValue(obj: any, key, separator = '.'): any {
    const keys = key.split(separator);
    return keys.reduce((acc, key) => acc && acc[key], obj);
  }

  /**
   * 递归设置嵌套属性的值
   * @param obj       目标对象
   * @param props     要设置的属性和值
   * @param separator 属性层级分隔符，默认为'.'
   */
  export function setNestedValue(obj: any, props: Record<any, any>, separator = '.'): void {
    for (const key in props) {
      const keys = key.split(separator);
      let i = 0;
      let len = keys.length;
      keys.reduce((acc, k) => {
        i++;
        if (i === len) {
          acc[k] = props[key];
          return;
        }
        if (!acc[k]) return;
        return acc[k];
      }, obj);
    }
  }

  /** 跟据数据的属性，可以指定任意深度的属性，对数据进行排序 */
  export function sortArraySuper(arr: any[], key: string[], sort?: number[]): any[] {
    if (!sort) {
      // 默认从小到大排序
      sort = new Array(key.length).fill(1);
    }

    // 一次性完成所有键的排序
    arr.sort((a, b) => {
      for (let i = 0; i < key.length; i++) {
        const aValue = Tool.getNestedValue(a, key[i], '.') ?? 0;
        const bValue = Tool.getNestedValue(b, key[i], '.') ?? 0;
        const comparison = aValue - bValue;
        if (comparison !== 0) {
          return comparison * sort[i];
        }
      }
      return 0;
    });

    return arr;
  }

  export function hasSameValue(arr1: number[], arr2: number[]): boolean {
    for (const value of arr1) {
      if (arr2.includes(value)) {
        return true;
      }
    }
    return false;
  }

  /** 打乱数据 */
  export function shuffleArr<T>(array: T[]): T[] {
    for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
  }

  /** 递归设置节点属性 */
  export function ergodicNoed(node: Node, func: (node: Node) => any, includeSelf = true) {
    if (includeSelf) {
      func(node);
    }
    node.children.forEach((child) => ergodicNoed(child, func));
  }

  /**
   * 判断字符串中是否包含某个子字符串
   * @param str 子字符串
   * @param value 字符串
   */
  export function judgeText(str, value): boolean {
    return value.toString().indexOf(str) > -1;
  }

  /**
   * 判断点是否在区域内
   * @param area 子字符串
   * @param x x坐标
   * @param pointY y坐标
   * @param point 坐标点
   */
  export function judgePointInArea(area: UITransform, x: number, pointY: number): boolean;
  export function judgePointInArea(area: UITransform, point: Vec3 | Vec2): boolean;
  export function judgePointInArea(area: UITransform, point: Vec3 | Vec2 | number, pointY?: number): boolean {
    let pos: Vec3;
    if (point instanceof Vec3) {
      pos = point;
    } else if (point instanceof Vec2) {
      pos = v3(point.x, point.y);
    } else {
      pos = v3(point, pointY);
    }
    area.convertToNodeSpaceAR(pos, pos);
    const xx = -area.anchorX * area.width;
    const yy = (1 - area.anchorY) * area.height;
    return pos.x >= xx && pos.x <= xx + area.width && pos.y <= yy && pos.y >= yy - area.height;
  }

  /**
   * 返回数组中第一个属性值的索引，如果不存在，则返回-1
   * @param arr   数组
   * @param prop  要查找的属性 用.分割可以获取任意深度的属性
   * @param value 要查找的值
   * @returns
   */
  export function indexOfByProp<T>(arr: T[], prop: string, value: any): number {
    for (let i = 0; i < arr.length; i++) {
      const temp = getNestedValue(arr[i], prop);
      if (temp === value) {
        return i;
      }
    }
    return -1;
  }

  /**
   * 返回数组中第一个属性值的索引和值，如果不存在，则返回-1，null
   * @param arr   数组
   * @param prop  要查找的属性 用.分割可以获取任意深度的属性
   * @param value 要查找的值
   * @returns
   */
  export function indexOfObjByProp<T>(arr: T[], prop: string, value: any): [number, T] {
    for (let i = 0; i < arr.length; i++) {
      const temp = getNestedValue(arr[i], prop);
      if (temp === value) {
        return [i, arr[i]];
      }
    }
    return [-1, null];
  }

  /**
   * 返回数组中第一个属性值的索引，如果不存在，则返回-1
   * @param arr   数组
   * @param prop  要满足的属性对象 例: {position.x: 1, position.y: 2} 用.分割可以获取任意深度的属性
   * @returns
   */
  export function indexOfByProps<T>(arr: T[], prop: Record<any, any>): number {
    for (let i = 0; i < arr.length; i++) {
      let boo = true;
      for (const key in prop) {
        const temp = getNestedValue(arr[i], key);
        if (temp !== prop[key]) {
          boo = false;
          break;
        }
      }
      if (boo) {
        return i;
      }
    }
    return -1;
  }

  /**
   * 返回数组中第一个属性值的索引和值，如果不存在，则返回-1，null
   * @param arr   数组
   * @param prop  要满足的属性对象 例: {position.x: 1, position.y: 2} 用.分割可以获取任意深度的属性
   * @returns
   */
  export function indexOfObjByProps<T>(arr: T[], prop: Record<any, any>): [number, T] {
    for (let i = 0; i < arr.length; i++) {
      let boo = true;
      for (const key in prop) {
        const temp = getNestedValue(arr[i], key);
        if (temp !== prop[key]) {
          boo = false;
          break;
        }
      }
      if (boo) {
        return [i, arr[i]];
      }
    }
    return [-1, null];
  }

  /**
   * 返回数组中所有属性值的索引，如果不存在，则返回[]
   * @param arr   数组
   * @param prop  要查找的属性 用.分割可以获取任意深度的属性
   * @param value 要查找的值
   * @returns
   */
  export function indexOfAllByProp<T>(arr: T[], prop: string, value: any): number[] {
    const result: number[] = [];
    for (let i = 0; i < arr.length; i++) {
      const temp = getNestedValue(arr[i], prop);
      if (temp === value) {
        result.push(i);
      }
    }
    return result;
  }

  /**
   * 返回数组中所有属性值的索引和值，如果不存在，则返回[]
   * @param arr   数组
   * @param prop  要查找的属性 用.分割可以获取任意深度的属性
   * @param value 要查找的值
   * @returns
   */
  export function indexOfObjAllByProp<T>(arr: T[], prop: string, value: any): [number, T][] {
    const result: [number, T][] = [];
    for (let i = 0; i < arr.length; i++) {
      const temp = getNestedValue(arr[i], prop);
      if (temp === value) {
        result.push([i, arr[i]]);
      }
    }
    return result;
  }

  /**
   * 返回数组中所有属性值的索引，如果不存在，则返回[]
   * @param arr   数组
   * @param prop  要满足的属性对象 例: {position.x: 1, position.y: 2} 用.分割可以获取任意深度的属性
   * @returns
   */
  export function indexOfAllByProps<T>(arr: T[], prop: Record<any, any>): number[] {
    const result: number[] = [];
    for (let i = 0; i < arr.length; i++) {
      let boo = true;
      for (const key in prop) {
        const temp = getNestedValue(arr[i], key);
        if (temp !== prop[key]) {
          boo = false;
          break;
        }
      }
      if (boo) {
        result.push(i);
      }
    }
    return result;
  }

  /**
   * 返回数组中所有属性值的索引和值，如果不存在，则返回[]
   * @param arr   数组
   * @param prop  要满足的属性对象 例: {position.x: 1, position.y: 2} 用.分割可以获取任意深度的属性
   * @returns
   */
  export function indexOfObjAllByProps<T>(arr: T[], prop: Record<any, any>): [number, T][] {
    const result: [number, T][] = [];
    for (let i = 0; i < arr.length; i++) {
      let boo = true;
      for (const key in prop) {
        const temp = getNestedValue(arr[i], key);
        if (temp !== prop[key]) {
          boo = false;
          break;
        }
      }
      if (boo) {
        result.push([i, arr[i]]);
      }
    }
    return result;
  }
}
